package com.ymttest.utils.compare;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;

import org.json.JSONObject;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class CompareHelper {
	private LoggerInterface logger;
	private List<Runnable> runList = new ArrayList<>();
	private CompareThreadPool compareThreadPool;
	private String defaultFormat = "yyyy-MM-dd'T'HH:mm:ss";
	
	
	private <T> T getBeanFromJson(JSONObject jsonObject, Class<T> clz, String dateFormat) {
		if (dateFormat == null || dateFormat.equals("")) {
			Gson gson = new GsonBuilder().setDateFormat(defaultFormat).create();
			return gson.fromJson(jsonObject.toString(), clz);
		} else {
			Gson gson = new GsonBuilder().setDateFormat(dateFormat).create();
			return gson.fromJson(jsonObject.toString(), clz);
		}

	}

	public CompareHelper(LoggerInterface logger, int threadNum) {
		this.logger = logger;
		AssertHelper.setLoggerInterface(logger);
		HttpClientUtil.setLoggerInterface(logger);
		compareThreadPool = new CompareThreadPool(threadNum);
	}

	public CompareHelper() {
		LogPathConf logPathConf = new LogPathConf(true);
		LoggerInterface logger = new LoggerHelper(logPathConf);
		this.logger = logger;
		AssertHelper.setLoggerInterface(logger);
		compareThreadPool = new CompareThreadPool(5);
		HttpClientUtil.setLoggerInterface(logger);
		compareThreadPool = new CompareThreadPool(5);
	}

	public <T> void setCompareApiData(CallAPI oldApi, CallAPI newApi, List<Map<String, Object>> paramMapList,
			Class<T> clz) {
		setCompareApiData(oldApi, null, newApi, null, paramMapList, clz);
	}

	public <T> void setCompareApiData(CallAPI oldApi, String oldTimeFormat, CallAPI newApi, String newTimeFormat,
			List<Map<String, Object>> paramMapList, Class<T> clz) {
		setCompareApiData(oldApi, oldTimeFormat, newApi, newTimeFormat, paramMapList, clz, null);
	}

	/*
	 * public <T> void setCompareApiData(CallAPI oldApi, String oldTimeFormat,
	 * CallAPI newApi, String newTimeFormat, List<Map<String, Object>>
	 * paramMapList, Class<T> clz, BiFunction<JSONObject, T, T> function) {
	 * runList.addAll(TaskFactory.generateTasks(paramMapList, x -> { JSONObject
	 * oldRet = oldApi.callAndReturn(x); JSONObject newRet =
	 * newApi.callAndReturn(x);
	 * 
	 * String desc = "ApiName: " + oldApi.getApiName() + ", Param:" +
	 * x.toString();
	 * 
	 * try { JSONObject oldData = oldRet.getJSONObject("Data"); JSONObject
	 * newData = newRet.getJSONObject("Data");
	 * 
	 * T oldBean = getBeanFromJson(oldData, clz, oldTimeFormat); if (function !=
	 * null) { oldBean = function.apply(oldData, oldBean); }
	 * 
	 * T newBean = getBeanFromJson(newData, clz, newTimeFormat);
	 * 
	 * AssertHelper.assertResultEqual(oldBean, newBean, desc); } catch
	 * (Exception e) { logger.failure(desc); } })); }
	 */

	public <T, V extends CallAPI> void setCompareApiData(V oldApi, 
														 String oldTimeFormat, 
														 V newApi, 
														 String newTimeFormat,
														 List<Map<String, Object>> paramMapList, 
														 Class<T> clz, 
														 BiFunction<JSONObject, T, T> beanHandlefunction) {
		setCompareApiData(oldApi,oldTimeFormat,newApi,newTimeFormat,paramMapList,clz,beanHandlefunction,"");
		
	}

	public <T, V extends CallAPI> void setCompareApiData(V oldApi, 
														 String oldTimeFormat, 
														 V newApi, 
														 String newTimeFormat,
														 List<Map<String, Object>> paramMapList, 
														 Class<T> clz, 
														 BiFunction<JSONObject, T, T> beanHandlefunction,
														 String rootNode) {
		
		
		runList.addAll(TaskFactory.generateTasks(paramMapList, x -> {
			
			
			String nodeName = (rootNode==null||rootNode.equals(""))?"Data":rootNode;
			
			String desc = "ApiName: " + oldApi.getApiName() + ", Param:" + x.toString();
			System.out.println(desc);
			
			
			try {
				

				JSONObject oldRet = oldApi.callAndReturn(x);
				JSONObject newRet = newApi.callAndReturn(x);
				
			
				if (oldRet.isNull(nodeName) && newRet.isNull(nodeName)) {
					logger.verifyEquals(true, true, desc + ",new return and old return are all null");
					return;
				} else if (!oldRet.isNull(nodeName) && newRet.isNull(nodeName)) {
					logger.failure(desc + ",new return is null and old is not null");
					return;
				} else if (oldRet.isNull(nodeName) && !newRet.isNull(nodeName)) {
					logger.failure(desc + ",old return is null and new is not null");
					return;
				}

				
				
				JSONObject oldData = oldRet.getJSONObject(nodeName);
				JSONObject newData = newRet.getJSONObject(nodeName);

				T oldBean = getBeanFromJson(oldData, clz, oldTimeFormat);
				if (beanHandlefunction != null) {
					oldBean = beanHandlefunction.apply(oldData, oldBean);
				}

				T newBean = getBeanFromJson(newData, clz, newTimeFormat);

				AssertHelper.assertResultEqual(oldBean, newBean, desc);
			} catch (Exception e) {
				logger.failure(desc, e);
			}
		}));
	}

	public <T, V extends CallAPI> void setCompareApiData(V oldApi, String oldTimeFormat, List<Map<String, Object>> oldParamMapList,
														 V newApi, String newTimeFormat, List<Map<String, Object>> newParamMapList,
														 Class<T> clz, BiFunction<JSONObject, T, T> beanHandlefunction,
														 String rootNode) {

		runList.addAll(TaskFactory.generateTasks(oldParamMapList,newParamMapList, (x,y) -> {

			String nodeName = (rootNode == null || rootNode.equals("")) ? "Data" : rootNode;

			String desc = "ApiName: " + oldApi.getApiName() + ", Param:" + x.toString();
			System.out.println(desc);

			try {
				JSONObject oldRet = oldApi.callAndReturn(x);
				JSONObject newRet = newApi.callAndReturn(y);

				if (oldRet.isNull(nodeName) && newRet.isNull(nodeName)) {
					logger.verifyEquals(true, true, desc + ",new return and old return are all null");
					return;
				} else if (!oldRet.isNull(nodeName) && newRet.isNull(nodeName)) {
					logger.failure(desc + ",new return is null and old is not null");
					return;
				} else if (oldRet.isNull(nodeName) && !newRet.isNull(nodeName)) {
					logger.failure(desc + ",old return is null and new is not null");
					return;
				}

				JSONObject oldData = oldRet.getJSONObject(nodeName);
				JSONObject newData = newRet.getJSONObject(nodeName);

				T oldBean = getBeanFromJson(oldData, clz, oldTimeFormat);
				if (beanHandlefunction != null) {
					oldBean = beanHandlefunction.apply(oldData, oldBean);
				}

				T newBean = getBeanFromJson(newData, clz, newTimeFormat);

				AssertHelper.assertResultEqual(oldBean, newBean, desc);
			} catch (Exception e) {
				logger.failure(desc, e);
			}
		}));
	}

	
	
	public void startCompare() {
		long useTime = compareThreadPool.runTask(runList);
		System.out.println(useTime);
	}

	public static void main(String args[]) {

	}

}